import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;

//watcher，链接订阅器跟UI的。
//当某个ui产生的时候，对应的wathcer也要一并产生。
export class UIWatcher{
    //当前这个监视器跟哪个订阅器相关联
    private _data:Object = null;   
    private _key:string = ''
    //关联的ui
    private _target:any = null;
    //回调函数
    private _cb:Function = null;

    //绑定组件
    private _comp:Component = null;
    constructor(data:Object,key:string,target:any,cb:Function,comp:Component){
        this._data = data;
        this._key = key;
        this._target = target;
        this._cb = cb;
        this._comp = comp;


        //需要知道data的key属性的dep是否已经有了，如果有了，把这个监听直接加入到key的dep中。
        //如果没有？直接创建一个dep，把当前监听者加入进去。
        //这里就可以获取到对应的dep，拿到这个dep之后，要把watcher对象加入到这个dep的数组中。 
        UIDepPool.instance.getDep(data,key).addWatcher(this);
    
    }

    //
    isContain(data:Object,key:string,target:any,cb:Function){
        return this._data = data&&this._key == key&&this._target == target&&this._cb == cb;
    }

    isEquals(watcherIn:UIWatcher){
        return this._data == watcherIn._data
            &&this._key == watcherIn._key
            &&this._target == watcherIn._target
            &&this._cb == watcherIn._cb;
    }

    notify(oldValue:any,newValue:any){
        //告诉跟这个watcher关联的UI去更新。 ==》 ui，某个毁掉函
        //this.关联UI.更新UI(oldValue,newValue);
        let args = [];
        for(let value of Array.from(arguments)){
            args.push(value);
        }
        this._comp&&args.push(this._comp);
        this._cb.apply(this._target,args);
    }

    removeSelf(){
        let dep = UIDepPool.instance.getDep(this._data,this._key);
        dep.removeWatcher(this);
    }
}




//dep类，监听某个对象的某个属性。
//监听属性
//对象，属性名字。
//watcher数组。
class UIDep{
    //这个订阅器监听的属性所在的对象。
    private _data:Object = null;
    get data(){
        return this._data;
    }
    //监听的对象的属性。
    private _key:string = '';
    get key(){
        return this._key;
    }
    //监听者数组，用于再对象发生修改的时候，通知对应ui（多个：coin  主页面，商店，背包）
    private _arrWatcher:UIWatcher[] = [];
    //某个数据，写成某个对象的某个属性， gameData.coin
    //已知存在这个数据的时候，就应该立即创建一个Dep专门用于监听这个数据（某个对象某个属性）。
    constructor(data:Object,key:string){
        this._data = data;
        this._key = key;

        //直接开始监听。 data里面key属性。
        this._definedProperty(this._data,this._key);
    }
    private _definedProperty(data:Object,key:string){
        let newValue = data[key];
        let self =this;
        Object.defineProperty(data,key,{
            get(){
                return newValue;
            },
            set(value:any){
                //把原来的值接收起来。
                let oldValue = newValue;
                //值修改。
                newValue = value;
                //修改完后要通知这个订阅器所管理的所有的监听者去告诉各自对应的ui去更新。
                self._notify(oldValue,newValue);
            }
        });
    }

    //这个订阅器是否是由某个data跟key得来的。
    isContain(data:Object,key:string){
        return this._data == data&&this._key == key;
    }

    isEquals(dep:UIDep){
        return this._data ==dep._data&&this._key == dep._key;
    }

    //添加watcher接口
    addWatcher(watcherIn:UIWatcher){
        //遍历watcher数组
        for(let value of this._arrWatcher){
            if(value.isEquals(watcherIn)){
                return;
            }
        }
        //如果不存在，那么直接push
        this._arrWatcher.push(watcherIn);
    }

    private _notify(oldValue:any,newValue:any){
        for(let watcher of this._arrWatcher){
            watcher.notify.apply(watcher,arguments);
        }
    }

    removeWatcher(watcherIn:UIWatcher){
        for(let i = 0;i < this._arrWatcher.length;i++){
            if(this._arrWatcher[i].isEquals(watcherIn)){
                this._arrWatcher.splice(i,1);
                break;
            }
        }

        //如果这个dep中不需要任何监听，那么干脆把他也从订阅器池中移除
        if(this._arrWatcher.length <=0){
            UIDepPool.instance.removeDep(this);
        }
    }
}


//订阅器池，专门用于存储订阅器，防止一个属性会new 多个dep
class UIDepPool{
    private static _instance:UIDepPool = null;
    static get instance(){
        if(!this._instance){
            this._instance = new UIDepPool();
        }
        return this._instance;
    }
    //容器，用于存储dep， 有几个属性就会有几个dep
    private _arrDep:UIDep[] = [];

    constructor(){

    }
    removeDep(dep:UIDep){
        for(let i = 0;i < this._arrDep.length;i++){
            if(this._arrDep[i].isEquals(dep)){
                this._arrDep.splice(i,1);
                return;
            }
        }
    }
    getDep(data:Object,key:string){
        for(let depIn of this._arrDep){
            if(depIn.isContain(data,key)){
                //成立意味着这个data和key的dep已经存在。
                return depIn;
            }
        }

        //代码执行到这里意味着没有data跟key关联的dep
        //直接new一个
        let dep = new UIDep(data,key);
        //不要忘记存储
        this._arrDep.push(dep);
        return dep;
    }
}